const {
    Tapable, AsyncSeriesHook, SyncBailHook, SyncHook, AsyncParallelHook
} = require('tapable')
const NormalModuleFactory = require("./NormalModuleFactory");
const Compilation = require("./Compilation");
const Stats = require("./Stats");
const path = require("path");
const mkdirp = require('mkdirp')

class Compiler extends Tapable {
    constructor(context) {
        super();
        this.context = context
        this.hooks = {
            done: new AsyncSeriesHook(['stats']),
            entryOption: new SyncBailHook(['context', 'entry']),

            beforeCompile: new AsyncSeriesHook(["params"]),
            compile: new SyncHook(["params"]),
            make: new AsyncParallelHook(["compilation"]),
            afterCompile: new AsyncSeriesHook(["compilation"]),

            thisCompilation: new SyncHook(["compilation", "params"]),
            compilation: new SyncHook(["compilation", "params"]),

            beforeRun: new AsyncSeriesHook(["compiler"]),
            run: new AsyncSeriesHook(["compiler"]),

            emit: new AsyncSeriesHook(['compilation'])
        }
    }

    emitAssets(compilation, callback) {
        // 1. 创建dist 2. 写文件
        // 定义一个执行文件生成操作的工具方法
        const emitFiles = (err) => {
            const assets = compilation.assets
            let outputPath = this.options.output.path

            for (let file in assets) {
                let source = assets[file]
                let targetPath = path.posix.join(outputPath, file)
                this.outputFileSystem.writeFileSync(targetPath, source, 'utf8')
            }

            callback(err)
        }
        // 创建目录后写入
        this.hooks.emit.callAsync(compilation, (err) => {
            mkdirp.sync(this.options.output.path) // 创建dist
            emitFiles()
        })
    }

    run(callback) {
        console.log('run 方法执行')
        const finalCallback = function (err, stats) {
            callback(err, stats)
        }

        const onCompiled = (err, compilation) => {
            console.log('onCompiled')
            finalCallback(err, new Stats(compilation))

            // 最终在这里将处理好的chunk写入指定文件并输出到dist
            this.emitAssets(compilation, (err) => {
                let stats = new Stats(compilation)
                finalCallback(err, stats)
            })
        }

        this.hooks.beforeRun.callAsync(this, (err) => {
            this.hooks.run.callAsync(this, (err) => {
                this.compile(onCompiled)
            })
        })
    }

    compile(callback) {
        const params = this.newCompilationParams()

        this.hooks.beforeRun.callAsync(params, (err) => {
            this.hooks.compile.call(params)
            const compilation = this.newCompilation(params)

            this.hooks.make.callAsync(compilation, (err) => {
                // console.log('make钩子监听触发')
                // callback(err,compilation)

                // 处理chunk
                compilation.seal((err) => {
                    this.hooks.afterCompile.callAsync(compilation, (err) => {
                        callback(err, compilation)
                    })
                })
            })
        })
    }

    newCompilationParams() {
        const params = {
            // normalModuleFactory = new NormalModuleFactory()
            normalModuleFactory: new NormalModuleFactory()
        }
        return params;
    }

    newCompilation(params) {
        const compilation = this.createCompilation()
        this.hooks.thisCompilation.call(compilation, params)
        this.hooks.compilation.call(compilation, params)
        return compilation
    }

    createCompilation() {
        // Compilation 代表某一次编译, Compiler 代表整个编译流程
        return new Compilation(this)
    }
}

module.exports = Compiler